

package tracker

// inflights的主要功能是记录当前节点己发出但未收到响应的MsgApp消息。
type Inflights struct {
	// inflights.buffer数组被当作一个环形数组使用，start字段中记录buffer中第一条MsgApp消息的下标。
	start int
	// 当前inflights实例中记录的MsgApp消息个数。
	count int
	// 当前inflights实例中能够记录的MsgApp消息个数的上限。
	size int
	// 用来记录MsgApp消息相关信息的数组，其中记录的是MsgApp消息中最后一条Entry记录的索引值。
	buffer []uint64
}

// NewInflights设置一个Inflights，最多允许“大小”的机上消息。
func NewInflights(size int) *Inflights {
	return &Inflights{
		size: size,
	}
}

// 克隆返回与接收器相同但不与接收器共享内存的*inflight。
func (in *Inflights) Clone() *Inflights {
	ins := *in
	ins.buffer = append([]uint64(nil), in.buffer...)
	return &ins
}

// inflights.add（）方法是用来记录发送出去的MsgApp消息
func (in *Inflights) Add(inflight uint64) {
	if in.Full() {	// 检测当前buffer数组是否已经被填充满了
		panic("cannot add into a Full inflights")
	}
	next := in.start + in.count	// 获取新增消息的下标
	size := in.size
	if next >= size {	// 环形队列
		next -= size
	}
	// 初始化时的buffer数纽较短，随着使用会不断进行扩容（两千剖，但其扩容的上限为size
	if next >= len(in.buffer) {
		in.grow()	// 扩容
	}
	in.buffer[next] = inflight	// 在next位置记录消息中最后一条Entry记录的索引位
	in.count++	//递增count字段
}

// 通过将飞行中的缓冲区增加一倍来增加飞行中的缓冲区。大小我们按需增长
// 而不是预先分配给飞机。处理具有
// /每个过程数千个筏组的系统的尺寸。
func (in *Inflights) grow() {
	newSize := len(in.buffer) * 2
	if newSize == 0 {
		newSize = 1
	} else if newSize > in.size {
		newSize = in.size
	}
	newBuffer := make([]uint64, newSize)
	copy(newBuffer, in.buffer)
	in.buffer = newBuffer
}

// 将指定消息及其之前的消息全部清空，释放inflights空间，让后面的消息继续发送。
func (in *Inflights) FreeLE(to uint64) {
	// 边界检测，检测当前inflights是否为空，以及参数to是否有效（
	if in.count == 0 || to < in.buffer[in.start] {
		// 窗口左侧外
		return
	}

	idx := in.start
	var i int
	for i = 0; i < in.count; i++ {	// 从start开始边历buffer
		if to < in.buffer[idx] { // 查找第一个大于指定索引值的位置
			break
		}

		size := in.size
		// 因为是环形队列，如采idx越界，则从0开始继续追历
		if idx++; idx >= size {
			idx -= size
		}
	}
	// i记录了此次释放的消息个数
	in.count -= i
	in.start = idx	// 从start～idx的所有消息都被释放（注意，是环形队列）
	if in.count == 0 {	// inflights中全部消息都被清空了，如l重直start
		in.start = 0
	}
}

// 释放inflights中第一个消息
func (in *Inflights) FreeFirstOne() { in.FreeLE(in.buffer[in.start]) }

// 判断当前inflights实例是否被填充满了，
func (in *Inflights) Full() bool {
	return in.count == in.size
}

// Count返回消息的数量。
func (in *Inflights) Count() int { return in.count }

// 重置将释放所有。
func (in *Inflights) reset() {
	in.count = 0
	in.start = 0
}
